﻿using PKTools.Database;
using System;
using System.Collections.Generic;
using System.Linq;

namespace PKTools.Generator
{
    public abstract class SysObj : SqlObject
    {
        [PK, Column("object_id")]
        public int
            ID { set; get; }
        public string
            Name { set; get; }
        public string
            Type { set; get; }
    }

    [Table("sys.objects")]
    public class SysObject : SysObj
    {
        public List<SysColumn>
            Columns;

        public override string ToString()
        {
            return this.Name;
        }
    }

    [Table("sys.foreign_keys")]
    public class SysFK : SqlObject
    {
        [Column("object_id")]
        public int
            ID { set; get; }
        public string
            Name { set; get; }
        [Column("referenced_object_id")]
        public int
            OneID { set; get;}
        [Column("parent_object_id")]
        public int
            ManyID { set; get; }

        public Dictionary<string, string>
            ColMap;

        public string OneName(string fkTable)
        {
            var
                allFKs = string.Concat(this.ColMap.Select(e => e.Key));
            var
                allPKs = string.Concat(this.ColMap.Select(e => e.Value));

            if (fkTable + allPKs == allFKs)
                return fkTable;

            return fkTable + '_' + allFKs;
        }

        public string ManyName(string fkTable, string pkTable)
        {
            var
                allFKs = string.Concat(this.ColMap.Select(e => e.Key));
            var
                allPKs = string.Concat(this.ColMap.Select(e => e.Value));

            if (pkTable + allPKs == allFKs)
                return fkTable + "List";

            return fkTable + "List_" + allFKs;
        }
    }

    [Table("sys.columns")]
    public class SysColumn : SysType
    {
        [PK, Column("column_id")]
        public int
            ID { set; get; }
        [Column("is_nullable")]
        public bool
            IsNullable { set; get; }
        [Column("is_identity")]
        public bool
            IsIdentity { set; get; }

        public bool
            IsPK;

        public string TypeString
        {
            get
            {
                var
                    type = GetCSharpType();

                return string.Format("{0}{1}", type.Name, this.IsNullable && type.IsValueType ? "?" : string.Empty);
            }
        }       

        public string AttributeString
        {
            get
            {
                var
                    temp = new List<string>();

                if (this.IsPK)
                    temp.Add("PK");
                if (this.IsIdentity)
                    temp.Add("Identity");

                return temp.Count == 0 ? string.Empty : '[' + string.Join(", ", temp) + ']';
            }
        }

        public override string ToString()
        {
            return this.Name;
        }
    }

    [Table("sys.procedures")]
    public class SysProcedure : SysObj
    {
        public List<SysParameter>
            Parameters;
    }

    [Table("sys.parameters")]
    public class SysParameter : SysType
    {
        [PK, Column("parameter_id")]
        public int
            ID { set; get; }
        [Column("is_output")]
        public bool
            IsOutput { set; get; }

        public string _Name
        {
            get { return this.Name.Remove(0, 1); }
        }

        public string TypeString
        {
            get
            {
                var
                    type = GetCSharpType();

                return string.Format("{0}{1}", type.Name, type.IsValueType ? "?" : string.Empty);
            }
        }

        public string CSharp()
        {
            return string.Format("{0}{1} {2}",
                this.IsOutput ? "out " : string.Empty,
                this.TypeString,
                this._Name);
        }
    }

    public abstract class SysType : SqlObject
    {
        [PK, Column("object_id")]
        public int
            ObjectID { set; get; }
        public string
            Name { set; get; }
        [Column("user_type_id")]
        public int
            UserType { set; get; }

        public Type GetCSharpType()
        {
            switch (MsSqlGenerator.Types[this.UserType])
            {
                case "tinyint":
                    return typeof(byte);
                case "smallint":
                    return typeof(Int16);
                case "int":
                    return typeof(Int32);
                case "bigint":
                    return typeof(Int64);
                case "decimal":
                    return typeof(decimal);
                case "float":
                    return typeof(double);
                case "varchar":
                case "nvarchar":
                case "text":
                case "ntext":
                    return typeof(string);
                case "bit":
                    return typeof(Boolean);
                case "time":
                    return typeof(TimeSpan);
                case "date":
                case "smalldatetime":
                case "datetime":
                    return typeof(DateTime);
                case "varbinary":
                    return typeof(byte[]);
                case "uniqueidentifier":
                    return typeof(Guid);
                default:
                    return typeof(string);
            }
        }
    }
}
